import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import sun.net.www.protocol.http.HttpURLConnection;

public class Downloader  {
	String url;
	String fName;
	volatile long  thread_total;
	String[] args;
	File file;
	FileChannel channel;
	File bpr;
	Rate rate;
	Map<Character, Long> bprMap= new HashMap<Character, Long>();
	RandomAccessFile bprser;
	ArrayList<BlockDownloader> list;
	public Downloader(String[] args) {
		this.args=args;
	}

	
	void complete(char fName) {
		synchronized (this) {
			--thread_total;
			System.out.println(fName + " " + new Date().toLocaleString()
					+ ""+" download completed! There are "+thread_total+" downloading");
			if (thread_total == 0){
				try {
					channel.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
				System.out.println(args[1]+" is download completed");
				//System.exit(0);
				
			}
				//merge();
		}
	}
	
	 static void split(String name,long size){
		 try {
			FileOutputStream out = new FileOutputStream(name, true);
			out.getChannel().truncate(size );
		} catch (Exception e) {
			e.printStackTrace();
		}
		 
	 }
	
	/**
	 * @param args
	 *            args[0] url args[1] fname args[2] thread total
	 */
	public static void main(String[] args) {
		if(args[0].startsWith("http://")){
			Downloader downloader=new Downloader(args); 
			
			try {
				downloader.download();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}else{
			split(args[0], Long.parseLong(args[1]));
		
		}

	}
	static long file_length=0;
	/*private void merge() {
		int count=0;
		tryMerge:	while(count<3){
		long total = 1;
		if (args.length > 2)
			total = Long.parseLong(args[2]);
		String name = args[1];
		File file = new File(name);
		long partSize=file.length();
		ArrayList<File> list= new ArrayList<File>();
		try {
			FileOutputStream stream = new FileOutputStream(file, true);
			FileChannel channel = stream.getChannel();
			for (int i = 1; i < total; i++) {
				File fileP = new File(name + i);
				list.add(fileP);
				FileInputStream in = new FileInputStream(fileP);
				FileChannel channelN = in.getChannel();
				System.out.println("merge "+fileP.getName()+", merge size:"+channel.transferFrom(channelN, file.length(), fileP.length()));
				in.close();
				stream.flush();
			}
			stream.flush();
			stream.close();
		    if(file.length()==file_length)
				for (File f:list) {
					f.delete();
				}
			System.out.println(name +" complete, real length:"+file_length+" file length:"+file.length() );
			break;
		} catch (Exception e) {
			System.err.println("download  Exception:"+e.getMessage());
			split(name, partSize);
			e.printStackTrace();
			count++;
			break tryMerge;
		}
		}
	}*/

	@SuppressWarnings("unchecked")
	ArrayList<BlockDownloader> download() throws Exception {
		
		list = new ArrayList<BlockDownloader>();
		thread_total = 1;
		String urls[]=args[0].split("`");
		int urlsL = urls.length;
		long length = length(urls[0]);
		if (args.length > 2)
			thread_total = Long.parseLong(args[2]);
		long mod = length % thread_total;
		long partial = length / thread_total;
		bpr=new File(args[1]+".bpr");
		try {
			bprser=new RandomAccessFile(bpr,"rwd");
			bprser.getChannel().lock();
			while(true)
				bprMap.put(bprser.readChar(),bprser.readLong());
			
		} catch (Exception e) {
			// TODO: handle exception
		}
		file=new File(args[1]);
		RandomAccessFile out = new RandomAccessFile(file,"rwd");
		out.setLength(length);
		channel=out.getChannel();
		channel.lock();
		
		BlockDownloader downloader = new BlockDownloader(this,urls[0], 'A', 0, partial);
		list.add(downloader);
		for (int i = 1; i < thread_total; i++) {
			long p = partial * (i);
			if (i == thread_total - 1) {
				downloader = new BlockDownloader(this,urls[i%urlsL], (char)('A'+i),
						p, partial + mod);
			} else {
				
				downloader = new BlockDownloader(this,urls[i%urlsL], (char)('A'+i),
						p, partial);
			}
			list.add(downloader);
			
		}
		rate=new Rate(this,list,length);
		start();		
		return list;
	}
	
	public void start(){
		for (BlockDownloader blockDownloader : list) {
			blockDownloader.start();
		}
		rate.start();
	}
	public void resume() {
		for (BlockDownloader blockDownloader : list) {
			blockDownloader.resume();
		}
		rate.suspend();
	}
	public void suspend(){
		rate.suspend();
		for (BlockDownloader blockDownloader : list) {
			blockDownloader.suspend();
		}
	}
	
	public void stop(){
		rate.stop();
		for (BlockDownloader blockDownloader : list) {
			blockDownloader.stop();
		}
	}
	
	public long length(String url1) {
		URLConnection connection;
		try {
			connection = new URL(url1).openConnection();
			int responseCode = ((HttpURLConnection)connection).getResponseCode();
			System.out.println(responseCode);
			if(responseCode>399)throw new RuntimeException("Error net");
			String headerField = connection.getHeaderField("Content-Length");// getHeaderField(i);
			Map<String, List<String>> headerFields = connection.getHeaderFields();
			System.out.println(headerFields);
			System.out.println(headerField);
			file_length=Long.parseLong(headerField);
		
			return file_length;
		} catch (Exception e) {
			if(1==1)
			throw new RuntimeException(e);
		} 

		return 0;

	}
	long getBpr(char key){
		synchronized (this) {
			Long p=bprMap.get(key);
			return p==null ? 0:p.longValue();
		}
	}
}
